#ifndef RX3DCORETYPES_H
#define RX3DCORETYPES_H

#include <shareddata.h>
#include <RX3D_global.h>
#include <metatype.h>

#include <Eigen/Eigen>


typedef Eigen::Matrix4d Matrix4D;
typedef Eigen::Vector3d Vector3D;
typedef Eigen::Vector4d Vector4D;
typedef Eigen::Quaterniond QuaternionD;

DECL_VARIANT_TYPE(Matrix4D)
DECL_VARIANT_TYPE(Vector3D)
DECL_VARIANT_TYPE(Vector4D)
DECL_VARIANT_TYPE(QuaternionD)

RX3D_EXPORT bool operator == (const QuaternionD& o1, const QuaternionD& o2);
RX3D_EXPORT bool operator != (const QuaternionD& o1, const QuaternionD& o2);

RX3D_EXPORT Matrix4D Compose(const Vector3D& pos,const Vector3D& scale,const QuaternionD& rotation);
RX3D_EXPORT void Decompose(const Matrix4D& mat,Vector3D& pos,Vector3D& scale,QuaternionD& rotation);


enum class RX3DShapeValueType :char
{
	ST_None,
	ST_Cuboid,
	ST_Sphere,
	ST_MeshFile
};

template<typename T>
struct __DECL_SHAPE_TYPE
{
	static constexpr RX3DShapeValueType type = RX3DShapeValueType::ST_None;
	static constexpr int size = 0;
};

#define DECL_SHAPE_TYPE(TYPE,ENUM) \
template<> \
struct __DECL_SHAPE_TYPE<TYPE> \
{ \
	static constexpr RX3DShapeValueType type = ENUM; \
	static constexpr int size = sizeof(TYPE); \
}; \


struct CubiodShape {
	double dx;
	double dy;
	double dz;
};
DECL_SHAPE_TYPE(CubiodShape, RX3DShapeValueType::ST_Cuboid);

struct SphereShape {
	double r;
};
DECL_SHAPE_TYPE(SphereShape, RX3DShapeValueType::ST_Sphere);

struct MeshFileShape {
	char file[255];
};
DECL_SHAPE_TYPE(MeshFileShape, RX3DShapeValueType::ST_MeshFile);

class RX3DShapeValue;
RX3D_EXPORT DataInputStream & operator >> (DataInputStream& s, RX3DShapeValue& d);
RX3D_EXPORT DataOutputStream & operator << (DataOutputStream& s, const RX3DShapeValue& d);

class RX3D_EXPORT RX3DShapeValue
{
public:
	RX3DShapeValue();
	~RX3DShapeValue();
	RX3DShapeValue(const RX3DShapeValue& data);
	template<typename T>
	RX3DShapeValue(const T& data){
		type = __DECL_SHAPE_TYPE<T>::type;
		int size = __DECL_SHAPE_TYPE<T>::size;

		this->data.resize(size);
		memcpy(this->data.data(), &data, size);
	}

	bool operator == (const RX3DShapeValue& o)const;
	bool operator != (const RX3DShapeValue& o) const;
	RX3DShapeValue& operator = (const RX3DShapeValue& o);

	template<typename T>
	void SetValue(const T& data) {
		type = __DECL_SHAPE_TYPE<T>::type;
		int size = __DECL_SHAPE_TYPE<T>::size;
		this->data.resize(size);
		memcpy(data.data(), &data, size);
	}

	template<typename T>
	const T& GetValue() const{

		if (type != __DECL_SHAPE_TYPE<T>::type)
		{
			throw std::bad_cast("RX3DShapeInfo::GetValue:Error Bad Cast");
		}

		return *(T*)data.data();
	}

	RX3DShapeValue GetType() const {
		return type;
	}

private:
	ByteArray data;
	RX3DShapeValueType type;

	friend DataInputStream & operator >> (DataInputStream& s, RX3DShapeValue& d);
	friend DataOutputStream & operator << (DataOutputStream& s, const RX3DShapeValue& d);
};

DECL_VARIANT_TYPE(RX3DShapeValue)

#endif // !RX3DCORETYPES_H
